package nm.aleksey.client;

import java.util.List;
import java.util.Set;
import java.util.logging.Logger;

import javax.validation.ConstraintViolation;

import nm.aleksey.client.events.EditAuthorEvent;
import nm.aleksey.client.widgets.AuthorEditor;
import nm.aleksey.shared.AuthorContext;
import nm.aleksey.shared.AuthorProxy;
import nm.aleksey.shared.TableRequestFactory;

import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.web.bindery.requestfactory.gwt.client.RequestFactoryEditorDriver;
import com.google.web.bindery.requestfactory.shared.Receiver;
import com.google.web.bindery.requestfactory.shared.Request;
import com.google.web.bindery.requestfactory.shared.RequestContext;

public class AuthorEditorWorkflow {
  interface Binder extends UiBinder<DialogBox, AuthorEditorWorkflow> {
    Binder BINDER = GWT.create(Binder.class);
  }

  interface Driver extends
      RequestFactoryEditorDriver<AuthorProxy, AuthorEditor> {
  }

  static private final Logger log = Logger.getLogger(AuthorEditorWorkflow.class
      .getName());

  static void register(final ClientFactory clientFactory) {
    clientFactory.getEventBus().addHandler(EditAuthorEvent.TYPE,
        new EditAuthorEvent.Handler() {
          @Override
          public void startEdit(AuthorProxy author,
              RequestContext requestContext) {
            new AuthorEditorWorkflow(clientFactory.getRequestFactory(), author)
                .edit(requestContext);
          }
        });
  }

  @UiField
  HTMLPanel contents;

  @UiField
  DialogBox dialog;

  @UiField(provided = true)
  AuthorEditor authorEditor;

  private final TableRequestFactory requestFactory;

  private final Driver editorDriver = GWT.create(Driver.class);

  private AuthorProxy author;

  public AuthorEditorWorkflow(TableRequestFactory requestFactory,
      AuthorProxy author) {
    this.requestFactory = requestFactory;
    this.author = author;
    authorEditor = new AuthorEditor();
    Binder.BINDER.createAndBindUi(this);
    contents.addDomHandler(new KeyUpHandler() {
      @Override
      public void onKeyUp(KeyUpEvent event) {
        if (event.getNativeKeyCode() == KeyCodes.KEY_ESCAPE) {
          onCancel(null);
        }
      }
    }, KeyUpEvent.getType());
  }

  /**
   * Construct and display the UI that will be used to edit the current
   * AuthorProxy, using the given RequestContext to accumulate the edits.
   */
  private void edit(RequestContext requestContext) {
    // editorDriver = GWT.create(Driver.class);
    editorDriver.initialize(getRequestFactory(), authorEditor);

    if (requestContext == null) {
      fetchAndEdit();
      return;
    }

    editorDriver.edit(author, requestContext);
    dialog.center();
    GWT.log(author.getName());
  }

  private void fetchAndEdit() {
    // The request is configured arbitrarily
    Request<AuthorProxy> fetchRequest = getRequestFactory().find(
        author.stableId());

    // Add the paths that the EditorDrives computes
    fetchRequest.with(editorDriver.getPaths());

    // We could do more with the request, but we just fire it
    fetchRequest.to(new Receiver<AuthorProxy>() {

      @Override
      public void onSuccess(final AuthorProxy author) {
        AuthorEditorWorkflow.this.author = author;
        // Start the edit process
        final AuthorContext context = getRequestFactory().authorRequest();
        // Display the UI
        edit(context);
        context.save(author);
        // context.addBook(author, null);
        // Configure the method invocation to be sent in the context
        // BookContext bookContext = getClientFactory().getRequestFactory()
        // .bookRequest();
        // final BookProxy book = bookContext.create(BookProxy.class);
        // book.setName("Alisa v strane chudes");
        // book.setIsdn("324-234-34");
        // bookContext.save(book).fire(new Receiver<Void>() {
        //
        // @Override
        // public void onSuccess(Void response) {
        // context.addBook(author, book);
        // }
        //
        // });
        // temporary ignore save it will be done at
        // context.addBook(author, null);
        // The context will be fire()'ed from the onSave() method
      }
    }).fire();
  }

  public TableRequestFactory getRequestFactory() {
    return requestFactory;
  }

  /**
   * Called by the cancel button when it is clicked. This method will just tear
   * down the UI and clear the state of the workflow.
   */
  @UiHandler("cancel")
  void onCancel(ClickEvent event) {
    dialog.hide();
  }

  /**
   * Called by the edit dialog's save button. This method will flush the
   * contents of the UI into the AuthorProxy that is being edited, check for
   * errors, and send the request to the server.
   */
  @UiHandler("save")
  void onSave(ClickEvent event) {
    // Flush the contents of the UI
    RequestContext context = editorDriver.flush();

    // Check for errors
    if (editorDriver.hasErrors()) {
      dialog.setText("Errors detected locally");
      return;
    }

    // context.update(author);

    // Send the request
    context.fire(new Receiver<Void>() {
      @Override
      public void onConstraintViolation(Set<ConstraintViolation<?>> errors) {
        // Otherwise, show ConstraintViolations in the UI
        dialog.setText("Errors detected on the server");
        editorDriver.setConstraintViolations(errors);
      }

      @Override
      public void onSuccess(Void response) {
        // Ugly bug it is required to emit EntityProxyChange event
        AuthorContext authorContext = getRequestFactory().authorRequest();
        authorContext.findAll().fire(new Receiver<List<AuthorProxy>>() {

          @Override
          public void onSuccess(List<AuthorProxy> response) {
            String result = "";
            for (AuthorProxy author : response) {
              result += " " + author.getName();
            }
            log.severe("Updated authors: " + result);
          }

        });
        dialog.hide();
      }
    });
  }
}
